using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
using System.Net.Sockets;


namespace Autobot
{
    abstract class Communicator
    {
        protected LogController log = null;
        protected static Communicator static_communicator = null;
        protected Thread thread = null;
        protected Queue<Message> message_queue = null;

        /**************************************************************
         * PUBLIC CLASS FUNCTIONS (API)
         **************************************************************/

        public Communicator()
        {
            static_communicator = this;
            
        }

        public abstract bool Initialize(LogController a_log, string a_ip);
        public abstract void Close();
        public abstract void SendMessage(UInt32 client_id, Message message);

        /**
         * Name: GetNextMessage()
         * Description: Gets the next message from the message queue.
         * Output: The message received, or null on error.
         */
        public Message GetNextMessage()
        {
            Message message = null;

            lock (message_queue)
            {
                if ((message_queue != null) && (message_queue.Count > 0))
                {
                    try
                    {
                        message = message_queue.Dequeue();
                    }
                    catch
                    {
                        message = null;
                    }
                }
            }

            return message;
        }

        public Communicator GetCommunicator()
        {
            return static_communicator;
        }

        /**************************************************************
         * PRIVATE CLASS FUNCTIONS
         **************************************************************/
        protected LogController GetLog()
        {
            return log;
        }

        protected void StartMessageLoop()
        {
            message_queue = new Queue<Message>();
            thread = new Thread(new ThreadStart(this.MessageLoop));
            thread.Start();
        }

        protected abstract void MessageLoop();

        /**
         * Name: Message ParseMessage(string a_string)
         * Description: Creates a 'Message' object out of a given string.
         * Input: The message string.  For example, the strng recieved by a socket. 
         * Output: A new 'Message' object, or NULL on failure.
         */
        protected Message ParseMessage(string a_string)
        {
            Message message = new Message();
            MessageField field;

            int i;
            int pos_start, pos_equal, pos_end;

            /* Input checks */
            if (a_string == null)
            {
                return null;
            }
            /***/

            /* Parse the message string */
            i = 0;
            //Console.WriteLine(a_string);
            bool ValidMessage = false;
            while (i < a_string.Length)
            {
                pos_start = char_scan(a_string, '[', i);
                pos_equal = char_scan(a_string, '=', i);
                pos_end = char_scan(a_string, ']', i);

                /* are we finished? */
                if (pos_start < 0 || pos_equal < 0 || pos_end < 0)
                {
                    break;
                }
                /***/

                /* verify correctness of values */
                if (pos_start >= pos_equal || pos_start >= pos_end ||
                    pos_equal >= pos_end)
                {
                    //invalid string, stop parsing
                    log.WriteLog("Garbage string: " + a_string);
                    return null;
                }
                /***/

                string name_string = a_string.Substring(pos_start + 1, (pos_equal - pos_start) - 1);
                string value_string = a_string.Substring(pos_equal + 1, (pos_end - pos_equal) - 1);

                if (name_string.CompareTo("0") == 0)
                {
                    if ( value_string.CompareTo(Message.magic_number.ToString()) != 0)
                    {
                        return null; //magic number failed
                    }
                    // Only messages containing the magic number should pass through
                    ValidMessage = true;
                }
                else if (name_string.CompareTo("type") == 0)
                {
                    int i2 = System.Convert.ToInt32( value_string );
                    MessageType t = (MessageType)i2;
                    message.SetMessageType(t);
                }
                else if (name_string.CompareTo("client") == 0)
                {
                    UInt32 i2 = System.Convert.ToUInt32( value_string );
                    message.SetClientID( i2 );
                }
                else
                {
                    field = new MessageField( name_string, value_string );
                    message.SetField( field );
                }

                i = pos_end + 1;
            }
            /***/

            /* return message */
            if (ValidMessage == true)
                return message;
            else
                return null;
            /***/
        }

        /**
         * Name: char_scan()
         * Description: Returns the index of the first 'a_char' found in 'a_string' starting at 'a_initial_position'
         * Output: The index of the first 'a_char' found, or -1 if it was not found
         */
        private int char_scan(string a_string, char a_char, int a_initial_position)
        {
            int i;
            for (i = a_initial_position; i < a_string.Length; i++)
            {
                if (a_string[i] == a_char)
                {
                    return i;
                }
            }

            return -1;
        }

        protected string Recv(StreamReader streamReader)
        {
            string theString = "err";
            try
            {
                theString = streamReader.ReadLine();
            }
            catch (IOException)
            {
                streamReader.Close();
                return null;
            }

            return theString;
        }

        protected void Send(StreamWriter streamWriter, string str)
        {
            try
            {
                streamWriter.WriteLine(str);
                streamWriter.Flush();
            }
            catch
            {
                this.GetLog().WriteLog("Send() error!");
                streamWriter.Close();
                return;
            }

            this.GetLog().WriteLog("Sent " + str);

        }

        protected string FindMyIPString(IPAddress[] ips, int numIPs)
        {
            for (int i = 0; i < numIPs; i++)
            {
                IPAddress CurrentIP = ips[i];
                string ip_address_str = CurrentIP.ToString();
                // We don't want any crap mac addresses or ipv6 junk
                if (!ip_address_str.Contains(":"))
                {
                    int Count = 0;
                    int Offset = 0;
                    // Count the number of periods in the line, IP addresses have three of them
                    while (Offset < ip_address_str.Length)
                    {
                        Offset = ip_address_str.IndexOf(".", Offset) + 1;
                        if (Offset == 0)
                            break;
                        Count++;
                    }
                    if (Count == 3)
                        return ip_address_str;
                }
            }
            return null;
        }
    }

}
